﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace DailyPhoto.Common
{
    public class PrivateThreadPool : IDisposable
    {
        public PrivateThreadPool(int maxThreads, bool background = true)
        {
            this.maxThreads = maxThreads;

            for (int i = 0; i < maxThreads; ++i)
            {
                var t = new Thread(() =>
                {
                    while (true)
                    {
                        var a = actions.Take();

                        lock (sync)
                        {
                            Monitor.PulseAll(sync);
                        }
                        if (a == null)
                            return;

                        a();
                        lock (sync)
                        {
                            Interlocked.Decrement(ref pendingTasks);
                            Monitor.PulseAll(sync);
                        }
                    }
                });

                if (Priority != null)
                    t.Priority = (ThreadPriority)Priority;

                if (background)
                    t.IsBackground = true;

                t.Start();

            }
        }

        public void Enqueue(Action a)
        {
            if (a == null)
                throw new DailyphotoException("Bad action");

            Interlocked.Increment(ref pendingTasks);
            actions.Add(a);
        }

        public void Dispose()
        {
            for (int i = 0; i < maxThreads; ++i)
                actions.Add(null);
        }

        public void WaitFull()
        {
            lock (sync)
            {
                while (actions.Count > 0)
                    Monitor.Wait(sync);
            }
        }

        public void WaitIdle()
        {
            lock (sync)
            {
                while (pendingTasks > 0)
                    Monitor.Wait(sync);
            }
        }

        public int PendingWork
        {
            get { return pendingTasks; }
        }

        private int pendingTasks;
        private int maxThreads;
        private BlockingCollection<Action> actions = new BlockingCollection<Action>();
        private object sync = new object();
        public ThreadPriority? Priority { get; set; }
    }
}
